package com.corewillsoft.fireflies.gameobjects;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

import com.corewillsoft.fireflies.gamelaws.LawType;
import com.corewillsoft.fireflies.gameobjects.fireflyraces.Color;
import com.corewillsoft.fireflies.gameobjects.fireflyraces.RaceType;
import com.corewillsoft.fireflies.sounds.SoundsManager;
import com.corewillsoft.fireflies.util.MathUtils;

// TODO @kshpachenko add support of previous firefly's position (required for rollback in collisions with other objects)
public class Firefly extends GameObject {

	private final float ENERGY_DECREASE_RATE = 0.003f;
	private final float RANDOM_MOVEMENT_RADIUS = 1.1f;

	private final float MAX_SPEED = 10f;
	private final float SLOWDOWN_RATE = 20f;

	private boolean isSaved;
	private boolean isAlive = true;

	private float previousX = 0f; // X coordinate in the previous step of game
	private float previousY = 0f; // Y coordinate in the previous step of game

	private float energy = 1.0f; // maximum energy is 1 (firefly explodes),
	// minimum energy is 0 (firefly is dead)
	private float speedX = 0f;
	private float speedY = 0f;

	private RaceType raceType = RaceType.BLACK;

	private final static List<LawType> activeCreatedLaws = Arrays
			.asList(LawType.COLLISION);

	private final static List<LawType> nonactiveCreatedLaws = Arrays
			.asList();

	private final static List<LawType> activeSupportedLaws = Arrays
			.asList(LawType.COLLISION,
					LawType.FAN_FIREFLY,
					LawType.FINGER_FORCE,
					LawType.FROG_FIREFLY,
					LawType.HORNET_FIREFLY,
					LawType.PORTAL_FIREFLY,
					LawType.INERTIA,
					LawType.UPDATE,
					LawType.WALL_FIREFLY);

	private final static List<LawType> nonactiveSupportedLaws = Arrays
			.asList(LawType.GRAVITY,
					LawType.FROG_FIREFLY,
					LawType.HORNET_FIREFLY,
					LawType.INERTIA,
					LawType.UPDATE);

	public Firefly(int x, int y, RaceType raceType) {
		super(x, y);
		this.raceType = raceType;
		init();
	}

	private void init() {
		setHeight(raceType.getFireflySize());
		setWidth(raceType.getFireflySize());
		updateColor();
	}

	private void updateColor() {
		Color color;
		if (isActive()) {
			color = raceType.getColor(energy); // law of color change depends on race
		} else if (!isAlive()) {
			color = raceType.getDeadColor();
		} else if (isSaved()) {
			color = raceType.getSavedColor();
		} else
			return;
		setColor(color.getRed(), color.getGreen(), color.getBlue());
	}

	public RaceType getRace() {
		return raceType;
	}

	public void setRaceType(RaceType raceType) {
		this.raceType = raceType;
	}

	@Override
	public void updateState() {

		if (!isSaved()) {
			if (energy > 0) {
				energy -= ENERGY_DECREASE_RATE;
			} else {
				setAlive(false);
			}
			updateColor();
		}
		updatePosition();
	}

	private void updatePosition() {
		/* save old position before movements */
		setPreviousPosition(getX(), getY());

		/* directed motion (with decreasing speed) */
		speedX = Math.signum(speedX)
				* (Math.abs(speedX) - Math.abs(speedX) / SLOWDOWN_RATE);
		speedY = Math.signum(speedY)
				* (Math.abs(speedY) - Math.abs(speedY) / SLOWDOWN_RATE);
		setPosition(getX() + speedX, getY() + speedY);

		/* random movement */
		if (isActive()) {
			Random rand = new Random();
			setPosition(getX() - RANDOM_MOVEMENT_RADIUS + 2
					* RANDOM_MOVEMENT_RADIUS * rand.nextFloat(),
					getY() - RANDOM_MOVEMENT_RADIUS + 2 * RANDOM_MOVEMENT_RADIUS
							* rand.nextFloat());
		}
	}

	private void setPreviousPosition(float previousX, float previousY) {
		this.previousX = previousX;
		this.previousY = previousY;
	}

	// set firefly's position to the coordinates in the previous step of the
	// game
	public void returnToPreviousPosition() {
		setPosition(previousX, previousY);
	}

	public float getEnergy() {
		return energy;
	}

	public void setEnergy(float energy) {
		if (!isActive()) {
			return;
		}

		this.energy = energy;

		if (energy <= 0 || energy >= 1) {
			setAlive(false);
		}

		updateColor();
	}

	// firefly is active in case if it is alive and is not in the portal
	@Override
	public boolean isActive() {
		boolean newActive = (isAlive() && !isSaved());
		return newActive;
	}

	public void setAlive(boolean alive) { 
		if (isAlive == alive) {
			return;
		}
		if (!alive) {
			SoundsManager.getInstance().playSpiritDead();
		}
		isAlive = alive;
		updateColor();
	}

	// alive gameobject
	public boolean isAlive() {
		return isAlive;
	}

	public void setSaved(boolean saved) {
		if (saved) {
			SoundsManager.getInstance().playSpiritSaved();
		}
		isSaved = saved;
		updateColor();
	}

	public boolean isSaved() {
		return isSaved;
	}

	public float getSpeedX() {
		return speedX;
	}

	public float getSpeedY() {
		return speedY;
	}

	public void setSpeed(float speedX, float speedY) {
		float newSpeedAbs = (float) MathUtils.vectorLength(speedX, speedY);
		if (MAX_SPEED >= newSpeedAbs) {
			this.speedX = speedX;
			this.speedY = speedY;
		} else {
			// normalize speed to the MAX_SPEED
			this.speedX = speedX * MAX_SPEED / newSpeedAbs;
			this.speedY = speedY * MAX_SPEED / newSpeedAbs;
		}
	}

	@Override
	protected List<LawType> getSupportedLaws() {
		if (isActive()) {
			return activeSupportedLaws;
		} else {
			return nonactiveSupportedLaws;
		}
	}

	@Override
	protected List<LawType> getCreatedLaws() {
		if (isActive()) {
			return activeCreatedLaws;
		} else {
			return nonactiveCreatedLaws;
		}
	}
}
